import { h, getCurrentInstance, defineAsyncComponent, ref, render, inject, VNode, reactive, onUnmounted } from 'vue'
import type { ComponentInternalInstance, Ref } from 'vue'
// import { layer } from "@layui/layer-vue";
import { layer } from '/@/components/layer/src'
import { nextId } from '/@/components/layer/src/utils'
import { isArray, isFunction, isString, debounce, mergeWith, meanBy, omit, cloneDeep } from 'lodash-es'
import { ElLoading } from 'element-plus'
import { buildUUID } from "@pureadmin/utils"
import { toCssSize } from '/@/utils/style'
import { anyFuncCall } from '/@/utils'

const modalCaches: Record<string, ModalCtor> = {}
let appContext = null

export function useModal(config: ModalConfig) {
  const intance = getCurrentInstance()

  if (appContext == null)
    appContext = intance.appContext;

  const modal = new ModalCtor(config)
  modalCaches[modal.id] = modal

  onUnmounted(() => {
    modal.close()
    delete modalCaches[modal.id]
  })

  return modal;
}

export function useInModal(config: ModalConfig = null) {
  const layerModalId = inject('layerModalId')
  const modal = modalCaches[layerModalId] as ModalCtor

  if (config) {
    modal.inModalConfig(config)
  }

  return modal;
}

class ModalCtor {
  id: string
  show: Ref<boolean>
  content: ModalContent
  contentVNode: VNode
  config: ModalConfig
  layerInstance: any
  events: any
  data: {}
  private loadingInstance: any
  private loadingCloseDebounce: Function

  constructor(config: ModalConfig) {
    this.id = nextId()
    this.show = ref(false)
    this.content = defineAsyncComponent(config.content)

    this.config = config
    this.events = {}

    this.loadingCloseDebounce = () => {
      this.loadingInstance && this.loadingInstance.close()
      this.loadingInstance = void 0
    }
  }

  open(config: ModalConfig = {}) {
    this.innerTrigger(config)
  }

  close() {
    this.layerInstance && this.layerInstance.exposed.close()
  }

  on(event: 'open' | 'close', callback: (instance: any) => void) {
    this.events[event] = this.events[event] || []
    this.events[event].push(callback)
  }

  loading(show: boolean, text: string = '请稍等...') {
    if (show) {
      if (!this.loadingInstance) {
        try {
          const target = this.layerInstance.ctx.$el.querySelector('.layui-layer')
          this.loadingInstance = ElLoading.service({ target, text })
        }
        catch {
          this.loadingInstance = ElLoading.service({ text })
        }
      }
    } else {
      this.loadingCloseDebounce()
    }

  }

  loadingWarp(func: () => void | Promise<void>) {
    const that = this
    return new Promise(reslove => {
      that.loading(true)
      anyFuncCall(func)
        .then(() => {
          reslove()
        })
        .finally(() => {
          that.loading(false)
        })
    })

  }

  inModalConfig(config: ModalConfig = {}) {
    this.innerTrigger(config)
  }

  private getOptions(config: ModalConfig = {}) {
    const { title, width, height, shadeClose, contentProps, contentOn } = {
      ...cloneDeep(this.config),
      ...cloneDeep(config),
      contentProps: config.contentProps
    }

    return {
      id: this.id,
      title: title,
      shadeClose,
      type: 1,
      maxmin: true,
      move: true,
      resize: true,
      anim: 150,
      isOutAnim: true,
      btn: false,
      shade: false,
      zIndex: 1002,
      appContext: appContext,
      area: [toCssSize(width), toCssSize(height)],
      content: this.contentVNode,
      beforeEnd: () => {
        if (this.loadingInstance) {
          return false
        }
      },
      end: () => {
        this.layerInstance = void 0
        if (isArray(this.events['close'])) {
          this.events['close'].forEach(callback => {
            callback.call(this.layerInstance, this.layerInstance)
          })
        }

      },
      success: () => {
        if (isArray(this.events['open'])) {
          this.events['open'].forEach(callback => {
            callback.call(this.layerInstance, this.layerInstance)
          })
        }
      }
    }
  }

  private innerTrigger(config: ModalConfig) {
    if (this.layerInstance) {
      const instance = this.layerInstance
      if (!instance) return;
      const exposeHandler = instance.exposed
      const ctx = instance.ctx
      if (ctx.min) {
        exposeHandler.minHandle()
      }
    } else {
      const { title, width, height, shadeClose, contentProps, contentOn } = {
        ...this.config,
        ...config,
        contentProps: config.contentProps
      }

      this.contentVNode = h(this.content, { ...contentProps, ...contentOn })
      const options = this.getOptions(config)
      this.id = layer.open(options)

      this.layerInstance = layer.findById(this.id).modalInstance.component

    }
  }

}